import os
import stat
import shutil
import common

from local_monitor import LocalMonitor
from plugin_base.provider import Provider, FileType, FileInfo, FileInfoExtended, SystemSize


class LocalProvider(Provider):
	"""Content provider for local files"""
	is_local = True
	protocols = ('file', )

	def is_file(self, path, relative_to=None):
		"""Test if given path is file"""
		real_path = path if relative_to is None else os.path.join(relative_to, path)
		return os.path.isfile(real_path)

	def is_dir(self, path, relative_to=None):
		"""Test if given path is directory"""
		real_path = path if relative_to is None else os.path.join(relative_to, path)
		return os.path.isdir(real_path)

	def is_link(self, path, relative_to=None):
		"""Test if given path is a link"""
		real_path = path if relative_to is None else os.path.join(relative_to, path)
		return os.path.islink(real_path)

	def exists(self, path, relative_to=None):
		"""Test if given path exists"""
		real_path = path if relative_to is None else os.path.join(relative_to, path)
		return os.path.exists(real_path)

	def unlink(self, path, relative_to=None):
		"""Unlink given path"""
		real_path = path if relative_to is None else os.path.join(relative_to, path)
		os.remove(real_path)

	def remove_directory(self, path, recursive, relative_to=None):
		"""Remove directory and optionally its contents"""
		real_path = path if relative_to is None else os.path.join(relative_to, path)
		if recursive:
			shutil.rmtree(real_path)
		else:
			os.rmdir(real_path)

	def remove_file(self, path, relative_to=None):
		"""Remove file"""
		real_path = path if relative_to is None else os.path.join(relative_to, path)
		os.remove(real_path)

	def create_file(self, path, mode=0644, relative_to=None):
		"""Create empty file with specified mode set"""
		real_path = path if relative_to is None else os.path.join(relative_to, path)
		open(real_path, 'w').close()
		self.set_mode(real_path, mode)

	def create_directory(self, path, mode=0755, relative_to=None):
		"""Create directory with specified mode set"""
		real_path = path if relative_to is None else os.path.join(relative_to, path)
		os.makedirs(real_path, mode)

	def get_file_handle(self, path, mode, relative_to=None):
		"""Open path in specified mode and return its handle"""
		real_path = path if relative_to is None else os.path.join(relative_to, path)
		return open(real_path, mode)

	def get_stat(self, path, relative_to=None, extended=False):
		"""Return file statistics"""
		real_path = path if relative_to is None else os.path.join(relative_to, path)

		try:
			# try getting file stats
			file_stat = os.stat(real_path)

		except:
			# handle invalid files/links
			result = FileInfo(
						size = 0,
						mode = 0,
						user_id = 0,
						group_id = 0,
						time_modify = 0,
						type = FileType.INVALID,
					)

			return result

		# get file type
		item_type = FileType.REGULAR

		if stat.S_ISDIR(file_stat.st_mode):
			item_type = FileType.DIRECTORY

		elif stat.S_ISLNK(file_stat.st_mode):
			item_type = FileType.LINK

		elif stat.S_ISBLK(file_stat.st_mode):
			item_type = FileType.DEVICE_BLOCK

		elif stat.S_ISCHR(file_stat.st_mode):
			item_type = FileType.DEVICE_CHARACTER

		elif stat.S_ISSOCK(file_stat.st_mode):
			item_type = FileType.SOCKET

		if not extended:
			# create normal file information
			result = FileInfo(
						size = file_stat.st_size,
						mode = stat.S_IMODE(file_stat.st_mode),
						user_id = file_stat.st_uid,
						group_id = file_stat.st_gid,
						time_modify = file_stat.st_mtime,
						type = item_type,
					)
		else:
			# create extended file information
			result = FileInfoExtended(
						size = file_stat.st_size,
						mode = stat.S_IMODE(file_stat.st_mode),
						i_mode = file_stat.st_mode,
						user_id = file_stat.st_uid,
						group_id = file_stat.st_gid,
						time_access = file_stat.st_atime,
						time_modify = file_stat.st_mtime,
						time_create = file_stat.st_ctime,
						type = item_type,
						device = file_stat.st_dev,
						inode = file_stat.st_ino
					)

		return result

	def set_mode(self, path, mode, relative_to=None):
		"""Set access mode to specified path"""
		real_path = path if relative_to is None else os.path.join(relative_to, path)
		os.chmod(real_path, mode)

	def set_owner(self, path, owner=-1, group=-1, relative_to=None):
		"""Set owner and/or group for specified path"""
		real_path = path if relative_to is None else os.path.join(relative_to, path)
		os.chown(real_path, owner, group)

	def rename_path(self, source, destination, relative_to=None):
		"""Rename file/directory within parents path"""
		if relative_to is None:
			real_source = source
			real_destination = destination
		else:
			real_source = os.path.join(relative_to, source)
			real_destination = os.path.join(relative_to, destination)

		try:
			os.rename(
					os.path.join(self._parent.path, real_source),
					os.path.join(self._parent.path, real_destination)
					)

		except OSError as error:
			# rename failed, notify user
			dialog = gtk.MessageDialog(
									self._parent,
									gtk.DIALOG_DESTROY_WITH_PARENT,
									gtk.MESSAGE_ERROR,
									gtk.BUTTONS_OK,
									_(
										'Unable to rename specified item. '
										'Check if you have permission to access '
										'specified path.\n\n{0}'
									).format(error)
								)
			dialog.run()
			dialog.destroy()

	def list_dir(self, path, relative_to=None):
		"""Get directory list"""
		real_path = path if relative_to is None else os.path.join(relative_to, path)
		return os.listdir(real_path)

	def get_system_size(self, path):
		"""Return system size information"""
		try:
			stat = os.statvfs(path)

			space_free = common.format_size(stat.f_bsize * stat.f_bavail)
			space_total = common.format_size(stat.f_bsize * stat.f_blocks)

			result = SystemSize(
						block_size = stat.f_bsize,
						block_total = stat.f_blocks, 
						block_available = stat.f_bavail,
						size_total = space_total,
						size_available = space_free
					)

		except:
			result = SystemSize(
						block_size = 0,
						block_total = 0, 
						block_available = 0,
						size_total = 0,
						size_available = 0
					)

		return result

	def get_monitor(self, path):
		"""Get file system monitor for specified path"""
		return LocalMonitor(self, path)
